/* $Id$ */
package org.crazydays.gameplan.view;


import java.beans.PropertyVetoException;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JDesktopPane;
import javax.swing.JInternalFrame;
import javax.swing.JMenuBar;
import javax.swing.JRootPane;
import javax.swing.JSplitPane;
import javax.swing.event.InternalFrameEvent;
import javax.swing.event.InternalFrameListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.crazydays.gameplan.Controller;
import org.crazydays.gameplan.db.Database;
import org.crazydays.gameplan.db.swing.JDatabaseTreePane;
import org.crazydays.gameplan.map.IGameMap;
import org.crazydays.gameplan.map.swing.JMapFrame;
import org.crazydays.gameplan.map.swing.MapMouseListener;
import org.crazydays.gameplan.model.IDatabaseObserver;
import org.crazydays.gameplan.model.Model;
import org.crazydays.gameplan.swing.JLogoDesktopPane;
import org.crazydays.i18n.Messages;

/**
 * View
 */
public class View
    extends JRootPane
    implements IDatabaseObserver, InternalFrameListener
{
    /** serial version uid */
    public final static long serialVersionUID =
        "$Id: GamePlan.java 37 2009-08-15 23:19:07Z aaron.day $".hashCode();

    /** logger */
    protected final static Logger LOGGER = LoggerFactory.getLogger(View.class);

    /** messages */
    protected final static Messages MESSAGES = Messages.getInstance();

    /** controller */
    protected Controller controller;

    /** model */
    protected Model model;

    /** menu bar */
    protected JMenuBar menuBar;

    /** split pane */
    protected JSplitPane splitPane;

    /** database tree pane */
    protected JDatabaseTreePane databasePane;

    /** desktop pane */
    protected JDesktopPane desktopPane;

    /** open game maps */
    protected Map<String, JMapFrame> openGameMaps =
        new HashMap<String, JMapFrame>();

    /** map frame mouse listener */
    protected MapMouseListener mapMouseListener;

    /**
     * View constructor.
     * 
     * @param controller Controller
     * @param model Model
     */
    public View(Controller controller, Model model)
    {
        this.controller = controller;
        this.model = model;
    }

    /**
     * Initialize.
     */
    public void initialize()
    {
        initializeListeners();
        buildMenus();
        initializeSplitPane();
    }

    /**
     * Initialize listeners.
     */
    protected void initializeListeners()
    {
        mapMouseListener = new MapMouseListener(controller);
    }

    /**
     * Build menus.
     */
    protected void buildMenus()
    {
        menuBar = new JMenuBar();
        menuBar.add(new FileMenu(controller));
        menuBar.add(new EditMenu(controller));
        menuBar.add(new ViewMenu(controller));
        menuBar.add(new HelpMenu(controller));

        setJMenuBar(menuBar);
    }

    /**
     * Initialize split pane.
     */
    protected void initializeSplitPane()
    {
        initializeDatabasePane();
        initializeDesktopPane();

        splitPane =
            new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, databasePane,
                desktopPane);
        splitPane.setOneTouchExpandable(true);
        splitPane.setDividerLocation(150);

        setContentPane(splitPane);
    }

    /**
     * Initialize desktop pane.
     */
    protected void initializeDesktopPane()
    {
        desktopPane = new JLogoDesktopPane();
    }

    /**
     * Initialize database pane.
     */
    protected void initializeDatabasePane()
    {
        databasePane = new JDatabaseTreePane(controller);
        model.addDatabaseObserver(databasePane);
        model.addGameMapObserver(databasePane);
    }

    /**
     * Open game map and bring it to the front.
     * 
     * @param gameMap GameMap
     */
    public void open(IGameMap gameMap)
    {
        String key = gameMap.getName();

        JMapFrame frame = openGameMaps.get(key);
        if (frame == null) {
            frame = new JMapFrame(controller, gameMap);
            frame.initialize();
            frame.addInternalFrameListener(this);
            frame.getComponent(0).addMouseListener(mapMouseListener);

            desktopPane.add(frame);
            openGameMaps.put(key, frame);
        }

        desktopPane.moveToFront(frame);

        try {
            frame.setSelected(true);
        } catch (PropertyVetoException e) {
            LOGGER.warn("Unable to set selected frame {0} {1}", key, e);
        }
    }

    /**
     * @param database Database
     * @see IDatabaseObserver#opened(Database)
     */
    @Override
    public void opened(Database database)
    {
    }

    /**
     * Close all open artifacts.
     * 
     * @param database Database
     * @see IDatabaseObserver#closed(Database)
     */
    @Override
    public void closed(Database database)
    {
        // close internal frames
        for (JInternalFrame frame : desktopPane.getAllFrames()) {
            frame.dispose();
        }

        // close maps
        openGameMaps.clear();
    }

    /**
     * Internal frame opened.
     * 
     * @param event Event
     * @see InternalFrameListener#internalFrameOpened(InternalFrameEvent)
     */
    @Override
    public void internalFrameOpened(InternalFrameEvent event)
    {
    }

    /**
     * Enable map options.
     * 
     * @param event Event
     * @see InternalFrameListener#internalFrameActivated(InternalFrameEvent)
     */
    @Override
    public void internalFrameActivated(InternalFrameEvent event)
    {
        JInternalFrame frame = event.getInternalFrame();

        if (frame instanceof JMapFrame) {
            controller.getActions().enableMapOptions();

            JMapFrame mapFrame = (JMapFrame) frame;
            if (mapFrame.isGridVisible()) {
                controller.getActions().getToggleGrid().setIconCross();
            } else {
                controller.getActions().getToggleGrid().setIconTick();
            }
        }
    }

    /**
     * Internal frame iconified.
     * 
     * @param event Event
     * @see InternalFrameListener#internalFrameIconified(InternalFrameEvent)
     */
    @Override
    public void internalFrameIconified(InternalFrameEvent event)
    {
    }

    /**
     * Internal frame deiconified.
     * 
     * @param event Event
     * @see InternalFrameListener#internalFrameDeiconified(InternalFrameEvent)
     */
    @Override
    public void internalFrameDeiconified(InternalFrameEvent event)
    {
    }

    /**
     * Disable map options.
     * 
     * @param event Event
     * @see InternalFrameListener#internalFrameDeactivated(InternalFrameEvent)
     */
    @Override
    public void internalFrameDeactivated(InternalFrameEvent event)
    {
        JInternalFrame frame = event.getInternalFrame();

        if (frame instanceof JMapFrame) {
            controller.getActions().disableMapOptions();
        }
    }

    /**
     * Internal frame closing.
     * 
     * @param event Event
     * @see InternalFrameListener#internalFrameClosing(InternalFrameEvent)
     */
    @Override
    public void internalFrameClosing(InternalFrameEvent event)
    {
    }

    /**
     * Remove game map from open game maps.
     * 
     * @param event Event
     * @see InternalFrameListener#internalFrameClosed(InternalFrameEvent)
     */
    @Override
    public void internalFrameClosed(InternalFrameEvent event)
    {
        JInternalFrame frame = event.getInternalFrame();
        if (frame instanceof JMapFrame) {
            openGameMaps.remove(((JMapFrame) frame).getGameMap().getName());
        }

        frame.removeInternalFrameListener(this);
    }

    /**
     * Toggle map grid.
     * 
     * @return Grid state
     */
    public boolean toggleGrid()
    {
        JInternalFrame frame = desktopPane.getSelectedFrame();

        if (!(frame instanceof JMapFrame)) {
            LOGGER.warn("Selected frame not JMapFrame");
            return false;
        }

        JMapFrame mapFrame = (JMapFrame) frame;
        mapFrame.setGridVisible(!mapFrame.isGridVisible());

        return mapFrame.isGridVisible();
    }
}
